Pythonの基本#

Pythonは、1990年代初頭ごろから公開され、それ以降使われ続けているプログラミング言語です。 インデントを採用した記法、動的な型付け等に特徴があり、可読性の高い直感的な表記が可能です。 また、関連するライブラリが多数存在しており、データ分析に関するプログラムを実装する際のデファクトスタンダードとなっています。

Pythonは非常に奥が深い言語ですので、その全てを本章で解説することはできません。 本章では、本書で紹介するデータビジュアライゼーション手法の理解に最低限必要な知識の紹介にとどめるため、詳細は別途公式サイトや専門書をご参照ください。

なお、本書で想定するPythonのバージョンは以下です。

Hide code cell content
# sysモジュールのインポート(詳細後述)
import sys

# pythonのバージョンを表示
sys.version
'3.11.6 | packaged by conda-forge | (main, Oct  3 2023, 11:57:02) [GCC 12.3.0]'

本章では、「基本データ型」「データ構造」「文法」という三つの柱を基にPythonを解説します:

  • 基本データ型 :プログラミングの世界で扱う最も基本的な要素は、データ自体です。ブール値、整数、浮動小数点数、文字列といった基本データ型は、この世界の基礎的な構成要素です。これらのデータ型を理解し、使いこなすことは、プログラミングの基礎を学ぶ第一歩となります。

  • データ構造 :次に、これらの基本データ型を組み合わせて、より複雑なデータ構造を形成します。リスト、タプル、辞書、集合といったデータ構造は、複数のデータを効率的に管理し、操作するための方法を提供します。基本データ型をブロックとして、これらのデータ構造はそれらを組み合わせ、整理するための「容器」のようなものです。

  • 文法 :最後に、Pythonの文法を学ぶことで、これらのデータ型やデータ構造を用いて、実際に複雑な処理を実行する方法を理解します。インデントによるブロックの構造、条件分岐やループ、関数やクラスの定義など、Pythonの文法をマスターすることにより、プログラムを効果的に構築できるようになります。

このように段階的に進めることで、しっかりとした基礎の上に応用力を築くことを目指しています。 本章を通じて、Pythonの基礎から応用までを体系的に学ぶきっかけとなれば幸いです。

基本データ型#

プログラミングの世界には、さまざまな種類のデータが存在します。 これらのデータを適切に扱うためには、まずそれぞれの「型」を理解することが不可欠です。 Pythonでは、基本データ型としてブール値、整数、浮動小数点数、文字列などがあります。 この節では、これらの基本データ型について学び、それぞれの特性と使い方を探求していきます。

ブール値は、プログラムの流れを制御する際に欠かせない真偽値を表します。 整数と浮動小数点数は、数値計算の基礎を提供し、文字列はテキストデータを扱う上での基本です。 これらのデータ型をマスターすることで、Pythonでのプログラミングがよりスムーズに、そして効率的になります。

それでは、Pythonにおけるこれらの基本データ型の魅力に触れ、プログラミングの基礎を一緒に固めていきましょう。

ブール値#

ブール値とは、真偽を表すデータ型で、真(True)と偽(False)の2つの値を持ちます。 Pythonではbool型と呼ばれます。 ブール値は条件判断や論理演算に使われることが多く、プログラムの制御フローを決定する際に重要な役割を果たします。 例えば、if文やwhile文の条件式で使用されます。

本書の9章では、アニメ化されたマンガ作品とそうでない作品を比較分析します。 このとき、例えば次のような形で、ブール値を用いて情報を管理すると便利でしょう。

Hide code cell content
# アニメ化されたかどうかを管理するフラグ
is_animated = True
Hide code cell content
is_animated
True

プログラミングをしていると、特定の条件をチェックする場面にしばしば遭遇します。 たとえば、「もしAがBより大きければ、この処理を行う」といった場合です。 このような条件を評価するためには、 論理式 を使用します。 論理式の評価結果は真(True)か偽(False)のいずれかのブール値で返されます。

Pythonでは、論理式を構成するためにいくつかの演算子が用意されています。 ここで、いくつかの基本的な演算子とその意味を簡単に解説します。

  • ==(等しい):二つの値が等しいかどうかをチェックします。例えば、5 == 5Trueになります。

  • !=(等しくない):二つの値が等しくないかどうかをチェックします。例えば、5 != 3Trueになります。

  • >(より大きい):左の値が右の値より大きいかどうかをチェックします。例えば、5 > 3Trueになります。

  • <(より小さい):左の値が右の値より小さいかどうかをチェックします。例えば、3 < 5Trueになります。

  • >=(以上):左の値が右の値以上かどうかをチェックします。例えば、5 >= 5Trueになります。

  • <=(以下):左の値が右の値以下かどうかをチェックします。例えば、3 <= 5Trueになります。

これらの演算子を使って、条件が真か偽かを評価することができます。 例えば、以下のように文字列同士を比較[剛昌, 1994]することも可能です。

Hide code cell content
# 二つの文字列が等しいかどうかを判定
"工藤新一" == "江戸川コナン"
False

論理式は、プログラムの中で「もし(if)」この条件が真ならば、といった条件分岐を作る際に非常に重要です。 条件分岐については後ほど詳細に解説します。

本書においては、特にPandasのデータフレームのフィルタリングに多く用います。 詳細はPandasの章で詳しく解説します。

整数#

整数とは、自然数、0、負の自然数を含む数のデータ型で、プログラム内で数値を扱う際によく使われます。 Pythonでは、int型(integer型)と呼ばれます。 整数同士の四則演算や剰余演算などが可能で、計算結果も整数になります。 ただし、除算演算子/は結果が浮動小数点数になるため、整数除算演算子//を使って商を整数で得ることができます。 また、演算子%を用いることで、除算の余りを求めることもできます。

Hide code cell content
# 加算
1 + 1
2
Hide code cell content
# 減算
3 - 1
2
Hide code cell content
# 乗算
5 * 4
20
Hide code cell content
# 除算
5 / 4
1.25
Hide code cell content
# 整数除算
5 // 4
1
Hide code cell content
# 余り
5 % 4
1

本書では様々な数値を整数型で表現します。 例えば、マンガ雑誌巻号の発売 年代 を求める際、発売年を単位年(年代を区切る単位、例えば10)で整数除算し、更に単位年をかけることで年代を求めています。

Hide code cell content
# 特定のマンガ雑誌巻号の発売年を設定
year = 1991
# 単位年を設定
unit_years = 10

# 単位年で整数除算し、更に単位年を乗算することで年代にまとめる
year // unit_years * unit_years
1990

また、8章では、一部のゲームパッケージが 数千800円台 で発売されることが多いという仮説を確かめるために、価格を1000で割った余りに対して可視化を行います。

Hide code cell content
# ゲームパッケージの発売価格
price = 5800

# 1000で割った余りを算出
price % 1000
800

このように、整数やその演算子を活用することで、効率的かつ読みやすい処理を実現することができます。

浮動小数点#

浮動小数点数とは、小数点を含む数のデータ型で、実数を表現するために用いられます。 Pythonでは、float型(floating-point型)と呼ばれます。 浮動小数点数同士の四則演算が可能で、計算結果も浮動小数点数になります。 ただし、浮動小数点数の計算は誤差が生じることがあり、数値計算において注意が必要です。

本書では様々な場面で浮動小数点を用います。 例えば、マンガ雑誌巻号内でのマンガ各話の掲載位置を表す値としてpage_start_positionがありますが、これは以下のように算出される小数です。

Hide code cell content
# マンガ各話の開始ページ数
page_start = 10

# マンガ雑誌巻号の最後のマンガ作品の終了ページ数
page_end_max = 468

# マンガ雑誌巻号内における開始ページの相対的な位置
page_start_position = page_start / page_end_max

# 変数の値を表示
page_start_position
0.021367521367521368

文字列#

文字列とは、文字や記号の連続したデータ型で、Pythonではstr型(string型)と呼ばれます。 文字列はシングルクォーテーション(')や ダブルクォーテーション(")で囲むことで定義できます。 また、三重クォーテーション('''または""")で囲むことで、 複数行にわたる文字列を定義することができます。

本書では非常に多くの文字列が登場します。マンガデータに限っただけでも

  • mcname: マンガ雑誌名

  • miname: マンガ雑誌巻号名

  • ccname: マンガ作品名

  • cename: マンガ各話名

  • crtname: マンガ作者名

が文字列です。 それぞれ次のように利用されています。

Hide code cell content
# data/cm/input/cm_ce.csv内の例
mcname = "週刊少年ジャンプ"
miname = "週刊少年ジャンプ 2017年 表示号数33"
ccname = "鬼滅の刃"
cename = "第70話 人攫い"
Hide code cell content
# ccnameに格納されている文字列を確認
ccname
'鬼滅の刃'

なお、本書では見栄えを整えるため、文字列の定義はダブルクオーテーション(")で統一しています。

本書で三重クオーテーションを用いる機会は多くありません。ここでは便宜上、ちはやふるに登場する札[由紀, 2008]を例に動作を確認します。

Hide code cell content
# 改行も含めて文字列として格納
card = """
からくれな
ゐにみつく
くるとは
"""
Hide code cell content
# cardの格納内容を確認
card
'\nからくれな\nゐにみつく\nくるとは\n'

\nは改行を表す文字です。

文字列に対しては、連結やスライス、置換、分割 などの操作が可能で、テキストデータの処理に広く使用されます。

本書では、各種データの前処理や、列名の変更等に文字列操作を用いています。例えば2章の散布図行列の可視化例では、長すぎる列名をreplaceメソッドを用いて置換しています。

Hide code cell content
# 元の長すぎる列名、これでは図内に収まらない
col = "8話目までの平均掲載位置"

# {元の文字列}.replace({置換対象の文字列}, {置換後の文字列})
# 以下の例では、「8話目までの」という部分を「」と置換、つまり削除している
col.replace("8話目までの", "")
'平均掲載位置'

また、文字列内に変数の値を埋め込むためのフォーマット機能も提供されており、結果の表示や変数の生成に役立ちます。

本書では特に f文字列 を用いたフォーマット機能を多用します。 これはPython 3.6以降に導入された機能であり、文字列の前にfを置き、カッコ{}内に直接変数名や式を書くことで、その場で評価し文字列に埋め込むことが可能です。

例えば、2章の散布図の可視化例では、min_nceという変数値に応じてX軸のラベルを変更できるようにf文字列を利用[1]しています。

Hide code cell content
# 可視化対象とする各話数
min_nce = 8

# X軸のラベル名
x_label = f"{min_nce}話までの平均掲載位置"
# X軸のラベル名を表示
x_label
'8話までの平均掲載位置'

min_nceを任意の値に変更した場合でも、x_label部分の実装を変更することなく対応が可能です。

Hide code cell content
# 可視化対象とする各話数を変更
min_nce = 10

# X軸のラベル名
x_label = f"{min_nce}話までの平均掲載位置"
# X軸のラベル名を表示
x_label
'10話までの平均掲載位置'

データ構造#

データ構造は、プログラミングにおいてデータを整理し、管理するための非常に重要な概念です。 前節で基本データ型について学んだあなたは、これらの型がプログラミングの基礎であることを理解しているでしょう。 しかし、実際のプログラムでは、これらの単一のデータ型だけでは対応できない複雑なデータを扱うことがよくあります。 そこで登場するのが「データ構造」です。

この章では、Pythonで利用可能な主要なデータ構造であるリスト、タプル、辞書、集合について学びます。 これらのデータ構造は、複数のデータを一つの単位として扱うことを可能にし、データの効率的な管理と操作を実現します。 それぞれのデータ構造がどのような特徴を持ち、どのような場面で最も効果的に使えるのかを探求していきましょう。

  • リスト:順序付けられたデータの集まりを扱います。要素の追加、削除、アクセスが自由にできる柔軟性が特徴です。

  • タプル:リストと似ていますが、一度作成されるとその内容を変更できません。不変なデータの集まりを安全に保持する場合に使用します。

  • 辞書:キーと値のペアでデータを格納します。キーによる高速なデータアクセスが必要な場合に便利です。

  • 集合:重複する要素を持たない要素の集まりです。データの存在を効率的にチェックするために使われます。

これらのデータ構造を理解し、適切に使いこなすことで、プログラムはより効率的に、そしてより強力になります。 それでは、Pythonのデータ構造の世界へ一緒に深く潜っていきましょう。

リスト#

リストは、複数のデータを順序付けて格納するためのデータ構造で、Pythonのlist型として提供されています。 リストは角括弧([])内に要素をカンマ(,)で区切って記述することで定義できます

本書では、例えば可視化対象とするマンガ雑誌やマンガ作品群を格納する用途で用いています。

Hide code cell content
# 分析対象とするマンガ雑誌名一覧
MCNAMES = [
    "週刊少年ジャンプ",
    "週刊少年マガジン",
    "週刊少年サンデー",
    "週刊少年チャンピオン",
]

上記はマンガデータの前処理にて、MADB Lab v1.0から抽出するマンガ雑誌一覧を定義したリストです。

リストは可変長であり、要素の追加や削除が自由に行えます。 また、インデックスを使って要素にアクセスすることができ、0から始まる整数で位置を指定します。 スライスを使って複数の要素を一度に取り出すことも可能です。

例えば、MCNAMES週刊少年キングを追加して五大少年誌[修治, 2020]とするためには、appendメソッドを使った実装があり得ます。

Hide code cell content
# MCNAMESに`週刊少年キング`を追加
MCNAMES.append("週刊少年キング")
# 更新後のMCNAMESを表示
MCNAMES
['週刊少年ジャンプ', '週刊少年マガジン', '週刊少年サンデー', '週刊少年チャンピオン', '週刊少年キング']

removeメソッドを用いて週刊少年キングを除外し、元に戻すこともできます。

Hide code cell content
# MCNAMESから`週刊少年キング`を除外
MCNAMES.remove("週刊少年キング")
# 更新後のMCNAMESを表示
MCNAMES
['週刊少年ジャンプ', '週刊少年マガジン', '週刊少年サンデー', '週刊少年チャンピオン']

+演算子を用いてリスト同士を結合することも可能です。

Hide code cell content
# MCNAMESと[`週刊少年キング`]を結合
MCNAMES + ["週刊少年キング"]
['週刊少年ジャンプ', '週刊少年マガジン', '週刊少年サンデー', '週刊少年チャンピオン', '週刊少年キング']

[i]のようにインデックスiを指定することで、i-1番目[2]の要素を取得することができます。 例えばMCNAME2番目の要素を取得する場合は、[1]を指定します。

Hide code cell content
# 2番目の要素を取得
MCNAMES[1]
'週刊少年マガジン'

リスト内の要素は異なるデータ型を混在させることができ、リスト自体もリストの要素として格納することができます(ネストされたリスト)。 この特性を利用して、多次元配列や他の複雑なデータ構造を表現することができます。

タプル#

タプルとは、複数のデータを順序付けて格納するデータ構造で、 リストと似ていますが、要素の変更ができないという点で異なります。 Pythonでは、tuple型として提供されています。 タプルは丸括弧(())内に要素をカンマ(,)で区切って記述することで定義できます。

残念ながら、本書ではタプルを利用する機会がほとんどありませんでした。 ここでは便宜上、ポケットモンスターで最初に選択するモンスター[Pokémon, 2024]を格納するタプルstartersを用いてその動作を説明します。

Hide code cell content
# ポケットモンスター赤緑青で最初に選択できるポケモン一覧
starters = ("フシギダネ", "ヒトカゲ", "ゼニガメ")
Hide code cell content
# モンスター一覧を表示
starters
('フシギダネ', 'ヒトカゲ', 'ゼニガメ')

startersの変更にトライしてみましょう。

Hide code cell content
# startersにappendできるか挑戦
starters.append("ピカチュウ")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[28], line 2
      1 # startersにappendできるか挑戦
----> 2 starters.append("ピカチュウ")

AttributeError: 'tuple' object has no attribute 'append'
Hide code cell content
# startersからフシギダネを除去できるか挑戦
starters.remove("フシギダネ")
Hide code cell content
# startersの2番目のポケモンを表示
starters[1]
'ヒトカゲ'
Hide code cell content
# startersの2番目のポケモンをリザードに変更
starters[1] = "リザード"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[30], line 2
      1 # startersの2番目のポケモンをリザードに変更
----> 2 starters[1] = "リザード"

TypeError: 'tuple' object does not support item assignment

追加、削除、更新の全てに失敗しました。 タプルは不変(immutable)であるため、 一度定義された要素の追加や削除ができません。 これにより、安全性が向上し、誤ってデータが変更されるリスクが低くなります。 タプルもインデックスを使って要素にアクセスできますが、要素の変更はできません。

辞書#

辞書とは、キーと値のペアでデータを格納するデータ構造で、 Pythonではdict型(dictionary型)として提供されています。 辞書は波括弧({})内にキーと値をコロン(:)で区切り、 ペアをカンマ(,)で区切って記述することで定義できます。

例えば本書では、Pandasのデータフレームの列名を変更する際に、変更前後の文字列の対応付けに用いています。 詳細はPandasの章で解説しますが、下記をrenameメソッドの引数として与えることで列名を一括変更可能です。

Hide code cell content
# 列名の変更前後を対応付ける辞書を定義
cols_rename = {
    "mcname": "マンガ雑誌名",
    "ccname": "マンガ作品名",
    "crtname": "マンガ作者名",
}
Hide code cell content
# 辞書の中身を表示
cols_rename
{'mcname': 'マンガ雑誌名', 'ccname': 'マンガ作品名', 'crtname': 'マンガ作者名'}

cols_rename["mcname"]のようにキーを指定することで値にアクセスできます。

Hide code cell content
# キーmcnameに対応する値を取得
cols_rename["mcname"]
'マンガ雑誌名'

同様の手順で値を更新することもできます。

Hide code cell content
# キーmcnameに対応する値を雑誌名に更新
cols_rename["mcname"] = "雑誌名"
# 更新結果を表示
cols_rename
{'mcname': '雑誌名', 'ccname': 'マンガ作品名', 'crtname': 'マンガ作者名'}

存在しないキーに対して値を対応付けることで、新しいペアを追加できます。

Hide code cell content
# {"cename": "マンガ各話名"}を追加
cols_rename["cename"] = "マンガ各話名"
# 更新結果を表示
cols_rename
{'mcname': '雑誌名', 'ccname': 'マンガ作品名', 'crtname': 'マンガ作者名', 'cename': 'マンガ各話名'}

あるいは、updateメソッドを用いて別の辞書と結合することができます。

Hide code cell content
# cols_renameと{"date": "発売日", "page_start_position": "掲載位置"}を結合
cols_rename.update({"date": "発売日", "page_start_position": "掲載位置"})
# 更新結果を表示
cols_rename
{'mcname': '雑誌名',
 'ccname': 'マンガ作品名',
 'crtname': 'マンガ作者名',
 'cename': 'マンガ各話名',
 'date': '発売日',
 'page_start_position': '掲載位置'}

また、辞書のキーは一意であり、重複するキーを持つことができません。 Python3.7以降、辞書は順序が保持されるようになったため、 キーを使って要素にアクセスすると、定義した順序で値が取得できます。

集合#

集合は、重複する要素を持たないデータ構造で、 Pythonではset型として提供されています。 集合は波括弧({})内に要素をカンマ(,) で区切って記述することで定義できます。 ただし、空の集合を定義する場合は、set()を使用します。

ここでは便宜上、葬送のフリーレンに登場する二つのパーティ[鐘人 and ツカサ, 2020]を例に集合を説明します。

Hide code cell content
# 勇者ヒンメル一行
himmels_party = {"ヒンメル", "ハイター", "アイゼン", "フリーレン", "フリーレン"}
# フリーレン一行
frierens_party = {"フリーレン", "フェルン", "シュタルク"}
Hide code cell content
# 勇者ヒンメル一行の構成員を確認
himmels_party
{'アイゼン', 'ハイター', 'ヒンメル', 'フリーレン'}

説明の都合上、himmels_partyにはフリーレンが重複していたのですが、集合として定義することで解消されます。

Hide code cell content
# フリーレン一行の構成員を確認
frierens_party
{'シュタルク', 'フェルン', 'フリーレン'}

集合を使うと、数学的な演算を簡単に行うことができます。 例えば、和集合(union)、積集合(intersection)、差集合(difference)、対称差(symmetric difference)などがあります。

Hide code cell content
# 和集合(二つの集合の全ての要素)
himmels_party | frierens_party
{'アイゼン', 'シュタルク', 'ハイター', 'ヒンメル', 'フェルン', 'フリーレン'}
Hide code cell content
# 積集合(二つの集合の両方に属する要素)
himmels_party & frierens_party
{'フリーレン'}
Hide code cell content
# 差集合(前者の集合に属する要素のうち、後者の集合に属さない要素)
himmels_party - frierens_party
{'アイゼン', 'ハイター', 'ヒンメル'}
Hide code cell content
# 対象差(二つの集合のうち片方にのみ属する要素)
himmels_party ^ frierens_party
{'アイゼン', 'シュタルク', 'ハイター', 'ヒンメル', 'フェルン'}

ただし、集合は順序を持たないため、要素の順序が重要な場合には適していません。

文法#

プログラミング言語を学ぶ上で、文法(Syntax)の理解は極めて重要です。 文法は言語の骨格のようなもので、正しい文法を使って初めて、プログラムは意図した通りに動作します。 この節では、Pythonの文法の基本に焦点を当て、プログラムの書き方とその構造を理解することを目指します。 具体的には、プログラムの基本的な構成要素であるインデントによるブロックの構造、条件分岐やループといった制御構造、そして関数やクラスの定義方法等について学びます。 これらの概念は、Pythonプログラミングの土台を築くものであり、効果的なプログラムを構築するために不可欠です。

インデント#

Pythonでは、インデント(字下げ)は単なるコードの見た目を整えるためだけではなく、コードの構造を定義する重要な役割を果たします。 他のプログラミング言語では波括弧({})などでコードブロックを示すことが多いですが、Pythonではインデント自体がコードブロックの開始と終了を示します。 これにより、Pythonのコードは読みやすく、理解しやすいものになります。

インデントが必要な場面として、例えば以下があります:

  • 条件分岐

  • ループ

  • クラス・関数定義

詳細はそれぞれの項目で解説します。

インデントとしてスペース4つを用いることが一般的ですが、タブを使うこともできます。 重要なのは、同じコードブロック内ではインデントの方法を統一することです。

#によるコメント#

これまで特に断りなく用いてきましたが、Pythonでは#記号の後に続く行の終わりまでがコメントとして扱われます。 コメントはPythonインタプリタによって無視されるため、プログラムの実行には影響しません。 これを使って、コードに関する説明や、一時的に実行を停止させたいコードの行をコメントアウトすることができます。

Hide code cell content
# これはコメントです
print("コメントは出力されません")

# print("このコードは実行されません")
print("このコードは実行されます")
コメントは出力されません
このコードは実行されます

条件分岐#

プログラムを書いていると、特定の条件に基づいて異なる処理を取りたい場面にしばしば遭遇します。 Pythonでは、ifelif(else ifの略)、elseキーワードを用いてこのような条件分岐を簡単に記述することができます。

例えば本書では、一つのマンガ作品に対応付けられている作者の数(n_crt)に応じて、共同制作単独制作を判断する処理で条件分岐を利用します。

Hide code cell content
# マンガ作者数を表す変数
n_crt = 1
# 作業スタイルを保持する変数、初期値はNone
work_style = None

# もしクリエイター数が1ならば、単独制作と判断
if n_crt == 1:
    work_style = "単独制作"
# もしクリエイター数が1より大きければ、共同制作と判断
elif n_crt > 1:
    work_style = "共同制作"
# 上記の条件に当てはまらない場合(予期しない値の場合)は、エラーを発生させる
else:
    raise ValueError("不正なマンガ作者数です。")

# 作業スタイルを確認
work_style
'単独制作'

上記のように、条件が複数ある場合にはelif(else if)を使って追加の条件を指定することができます。 最終的な「それ以外の場合」にはelse以下の処理が実行されます。

反復処理#

プログラミングにおいて、同じまたは似たような処理を繰り返し行いたい場合がよくあります。 このような繰り返し処理を実現するために、Pythonではforループやwhileループといった構文を提供しています。 これらを使うことで、コードの効率を大幅に向上させることができます。

forループは、あらかじめ決められた回数の繰り返し処理を実行する際に便利です。

Hide code cell content
# forループを使って0から4までの数字を出力
for counter in range(5):
    # 現在のカウンターの値を出力
    print(counter)
0
1
2
3
4

上記の例では、forループを使って0から4までの整数を表示しています。 range関数は、指定された範囲の数値のシーケンスを生成します。forループと合わせてよく用いられる関数です。

本書では、例えばマンガ雑誌別に可視化を行う際にforループを利用しています。

Hide code cell content
# マンガ雑誌名を格納するリストを定義
mcnames = [
    "週刊少年ジャンプ",
    "週刊少年マガジン",
    "週刊少年サンデー",
    "週刊少年チャンピオン",
]

# mcnameの要素を一つずつ取り出して実行
for mcname in mcnames:
    # mcnameごとに集計し可視化
    # (実際には様々な処理を行うがprintで割愛)
    print(f"{mcname}に関する可視化")
週刊少年ジャンプに関する可視化
週刊少年マガジンに関する可視化
週刊少年サンデーに関する可視化
週刊少年チャンピオンに関する可視化

単に要素を取り出すだけでなく、その要素が何番目であるか(インデックス)も知りたい場合があります。 Pythonでは、enumerate関数を使うことでこのような処理を簡単に実現できます。

例えば本書では、Plotlyのmake_subplotを用いて\(n\)\(m\)列の可視化を行う際に、

  • 左側(\(1\)列目)に位置するサブプロットに限ってY軸のラベルと目盛を表示したい

  • 下側(\(m\)行目)に位置するサブプロットに限ってX軸のラベルと目盛を表示したい

ということがよくあります。 このとき、インデックスの値を取得し、列数と行数を評価した上で条件分岐させています。

Hide code cell content
# 列数と行数の設定
cols = 2
rows = 2

# mcnamesの各要素に対してenumerateを使ってループ処理
for i, mcname in enumerate(mcnames):
    # 現在の要素の行位置を計算(1から始まるように+1)
    row = i // cols + 1
    # 現在の要素の列位置を計算(1から始まるように+1)
    col = i % cols + 1
    # mcnameとその位置(行と列)を含むテキストを作成
    text = f"{mcname}{row}行目, {col}列目):"

    # 最後の行であれば、X軸ラベル・目盛追加のメッセージを追加
    if row == rows:
        # (実際には様々な処理を行うがtextで割愛)
        text += "X軸ラベル・目盛追加、"
    # 最初の列であれば、Y軸ラベル目盛追加のメッセージを追加
    if col == 1:
        # (実際には様々な処理を行うがtextで割愛)
        text += "Y軸ラベル・目盛追加、"
    # 最終的なテキストを出力
    print(text)
週刊少年ジャンプ(1行目, 1列目):Y軸ラベル・目盛追加、
週刊少年マガジン(1行目, 2列目):
週刊少年サンデー(2行目, 1列目):X軸ラベル・目盛追加、Y軸ラベル・目盛追加、
週刊少年チャンピオン(2行目, 2列目):X軸ラベル・目盛追加、

whileループは、特定の条件が真(True)である間、繰り返し処理を行います。 条件が偽(False)になった時点でループから抜け出します。

Hide code cell content
# whileループの開始
# カウンター変数を0で初期化
counter = 0

# カウンター変数が5未満の間、ループを続ける
while counter < 5:
    # 現在のカウンターの値を出力
    print(counter)
    # カウンターの値を1増やす(これがないと無限ループになってしまう)
    counter += 1
0
1
2
3
4

上記の例では、whileループを使って0から4までの整数を表示しています。 whileループでは容易に無限ループに陥ってしまうため、注意が必要です。

内包表記#

Pythonにおける内包表記は、リスト、辞書、集合などのコレクション型の要素を生成する簡潔な方法です。 内包表記を使用することで、簡単で読みやすく、かつ高速なコードを書くことができます。 以下にそれぞれの例を示します。

Hide code cell content
# リスト内包表記
squares = [x**2 for x in range(5)]
print(squares)  # 出力: [0, 1, 4, 9, 16]

# 辞書内包表記
squares_dict = {x: x**2 for x in range(5)}
print(squares_dict)  # 出力: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 集合内包表記
squares_set = {x**2 for x in range(5)}
print(squares_set)  # 出力: {0, 1, 4, 9, 16}
[0, 1, 4, 9, 16]
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
{0, 1, 4, 9, 16}

上記の例では、リスト内包表記・辞書内包表記・集合内包表記を使って0から4までの整数の二乗を要素に持つデータ型を定義しています。

条件分岐と組み合わせることで、内包表記はより一層強力になります。 本書では、例えば特定の条件を満たす要素を抽出するフィルタとして内包表記を利用しています。

Hide code cell content
# 合計各話数の多いマンガ作品上位5作品と、その作品が属するマンガ雑誌の対応け
ccname2mcname = {
    "こちら葛飾区亀有公園前派出所": "週刊少年ジャンプ",
    "はじめの一歩": "週刊少年マガジン",
    "名探偵コナン": "週刊少年サンデー",
    "ONE PIECE": "週刊少年ジャンプ",
    "MAJOR": "週刊少年サンデー",
}

# そのうち、週刊少年マガジンに属するマンガ作品のみを抽出
ccname_magazine = [
    # itemsメソッドでキーと値を一つずつ抽出するが、mcnameの条件を満たすもののみ列挙
    ccname
    for ccname, mcname in ccname2mcname.items()
    if mcname == "週刊少年マガジン"
]
# 抽出結果を確認
ccname_magazine
['はじめの一歩']

上記の例では、ccname2mcnameという所与の辞書中から、週刊少年マガジンに属するマンガ作品を抽出する処理をリスト内包表記で書きました。

関数#

Pythonにおける関数は、一連の処理をまとめて再利用可能なコードブロックとして定義する方法です。 defキーワードを使って関数を定義し、関数名と()を使って関数を呼び出すことができます。 以下にその例を示します。

Hide code cell content
def calculate_kokusen_power(base_power: float) -> float:
    """
    マンガ「呪術廻戦」に登場する「黒閃」現象下の威力を計算する関数
    「黒閃」現象下では、通常の(平均)2.5乗の威力になると言われている

    Parameters
    ----------
    base_power : float
        通常時の威力

    Returns
    -------
    float
        「黒閃」現象下の威力の平均
    """
    kokusen_power = base_power ** 2.5
    return kokusen_power

関数の定義は、defキーワードから始まります。 続けて関数名(ここではcalculate_kokusen_power)を指定し、括弧()内に引数(ここではbase_power)を定義します。 引数には、型ヒント(ここではfloat)を付けることで、期待される引数の型を明示することができます。

関数のブロック内には、関数の処理を記述します。 この例では、base_powerを2.5乗することで「黒閃」現象の威力を計算しています[3]

return文を使って、関数の返り値を指定します。 ここでは、計算された「黒閃」現象下での威力を返しています。 また、返り値の型ヒントも->の後に指定することができます。

関数の定義直下にある三重クオートで囲まれた文字列は、docstringと呼ばれます。 詳細はコラムを参照ください。

関数を呼び出すには、関数名の後に括弧()を付け、引数を渡します。

Hide code cell content
# 黒閃現象下で、通常時50の威力がどう変化するか計算
calculate_kokusen_power(50)
17677.66952966369

このように、関数を定義することで、複雑な処理を分かりやすい名前で呼び出すことができ、コードの可読性と再利用性が向上します。 また、型ヒントやdocstringを活用することで、関数の利用者にとって理解しやすいインターフェースを提供することができます。

Pythonにおけるドキュメンテーション文字列:docstring

Pythonでは、コードの中に直接ドキュメンテーション文字列、通称「docstring」を書くことができます。 docstringは、関数、クラス、モジュールなど、コードの特定の部分がどのような働きをするのかを説明するために使われます。 この文字列は三重クォート(“””)で囲まれ、定義の直下に記述されます。

docstringの主な目的は、コードの使い方や意図を明確にすることです。 これにより、他のプログラマーがあなたのコードを読んだり、使ったりする際に、その機能を素早く理解できるようになります。 また、docstringを前提としたツールを利用することでドキュメンタリー作成を効率化できます。 例えば、Pythonにはhelp()関数や自動ドキュメント生成ツール(例えばSphinx)があり、docstringを使ってこれらのツールから情報を引き出すことができます。

docstringにはいくつかスタイルがあります:

  • Googleスタイル

  • reStructuredTextスタイル

  • NumPy/SciPyスタイル

各スタイルの詳細は割愛しますが、本書ではPandasも採用しているNumpy/SciPyスタイルを採用します。

クラス#

Pythonにおけるクラスは、オブジェクト指向プログラミングの中核をなす概念であり、 関連するデータ(属性)と操作(メソッド)をまとめたカスタムデータ型を定義するために使用されます。 classキーワードを使ってクラスを定義し、クラスからインスタンス(オブジェクト)を生成して操作することができます。

本書では、(ライブラリの利用を通して多大なる恩恵を受けていますが)クラスを自ら定義して用いる機会はありません。 しかし、クラスは非常に重要な概念ですので、その概要を理解しておくべきだと筆者は考えます。 ここでは便宜上、手塚治虫さんのスター・システムを例に解説します。

スター・システム は元々ハリウッドで、特定のスター俳優を映画の中心にする製作手法を指します。 手塚治虫さんが採用したマンガ版スター・システムでは、キャラクターが異なる作品で様々な役を演じることで、実際の俳優のように扱われます。 これにより、同一キャラクターが主人公から脇役、悪役まで幅広い役割を果たすことが可能になり、読者に新鮮な楽しみを提供しました[TEZUKA PRODUCTIONS, 2024]

以下では、スター(キャラクター)を管理するためのクラスStarを作成します。

Hide code cell content
class Star:
    # Starクラスの初期化メソッド
    def __init__(self, name):
        # インスタンス変数nameにキャラクターの名前を格納
        self.name = name
        # インスタンス変数worksに、キャラクターが出演する作品のリストを格納するための空リストを初期化
        self.works = []

    # キャラクターの出演作品を追加するメソッド
    def add_works(self, year, title):
        # worksリストに辞書形式で作品の年とタイトルを追加
        self.works.append({"year": year, "title": title})

    # キャラクターの出演作品一覧を出力するメソッド
    def print_works(self):
        # 出演作品一覧のヘッダーを出力
        print("主な出演作品")
        # worksリストをループして、各作品の年とタイトルをフォーマットして出力
        for work in self.works:
            print(f"- {work['year']}年: {work['title']}")
        print("...")

では、実際にいくつかスター(キャラクター)を作成してみましょう。

Hide code cell content
# ブラックジャック(https://tezukaosamu.net/jp/character/647.html)
bj = Star("ブラック・ジャック")
bj.add_works(year=1973, title="ブラック・ジャック")
bj.add_works(year=1975, title="鉄腕アトム /アトム二世 (科学省)")
Hide code cell content
# ブラック・ジャックの出演作品を出力
bj.print_works()
主な出演作品
- 1973年: ブラック・ジャック
- 1975年: 鉄腕アトム /アトム二世 (科学省)
...
Hide code cell content
# アトム(https://tezukaosamu.net/jp/character/25.html)
atom = Star("アトム")
atom.add_works(year=1951, title="鉄腕アトム")
atom.add_works(year=1952, title="ロック冒険記 (鳥人との会議に参加)")
Hide code cell content
# アトムの出演作品を出力
atom.print_works()
主な出演作品
- 1951年: 鉄腕アトム
- 1952年: ロック冒険記 (鳥人との会議に参加)
...

クラスを作成することで、全てのスター(キャラクター)に共通する処理を効率的に実装することが可能になりました。

import文によるインポート#

Pythonでプログラミングを行う際、多くの場合、標準ライブラリやサードパーティ製のライブラリのコードを利用します。 これらのコードを自分のプログラムに取り込むためには、import文を使って「インポート」する必要があります。

モジュール、パッケージ、そしてライブラリ

インポートと関連して、Pythonに登場するモジュール、パッケージ、そしてライブラリ[4]という用語の定義について触れておきましょう。

モジュール とは、Pythonで書かれたスクリプトファイルのことを指します。 一つのファイルが一つのモジュールに相当し、関数やクラス、変数などを含むことができます。 例えば、mathモジュールには数学的な関数が定義されており、import mathとすることでこれらの関数を使うことができます。

パッケージ は、複数のモジュールを含むディレクトリ(フォルダ)のことです。 パッケージは、モジュールを論理的に整理して管理するための仕組みを提供します。 Pythonでは、__init__.pyファイル(空でも構わない)が含まれるディレクトリをパッケージとして扱うことができます。 例えば、numpypandasなどのライブラリは、多数のモジュールで構成されたパッケージです。

ライブラリ とは、特定の機能を提供するために再利用可能なコードの集まりを指します。 ライブラリは、一つ以上のモジュールやパッケージで構成されることがあります。 Pythonの標準ライブラリはPythonに付属しており、サードパーティ製のライブラリはpipなどのパッケージ管理ツールを通じてインストールすることができます。

事前準備として、以下の関数を定義しておきます。

Hide code cell source
# plotlyの図を表示するための関数を定義
RENDERER = "plotly_mimetype+notebook"


def show_fig(fig):
    """
    所定のレンダラーを用いてplotlyの図を表示
    Jupyter Bookなどの環境での正確な表示を目的とする

    Parameters
    ----------
    fig : Figure
        表示対象のplotly図

    Returns
    -------
    None
    """

    # 図の周囲の余白を設定
    # t: 上余白
    # l: 左余白
    # r: 右余白
    # b: 下余白
    fig.update_layout(margin=dict(t=25, l=25, r=25, b=25))

    # 所定のレンダラーで図を表示
    fig.show(renderer=RENDERER)

以降、import文のバリエーションとして、主要な3パターンを紹介します。

importのみを使う場合#

この方法では、モジュールやパッケージ全体をインポートします 。インポートされたモジュールやパッケージ内の関数やクラスを使うには、{モジュール名}.{関数名}の形式でアクセスします。

Hide code cell content
# pandasパッケージ全体をインポート
import pandas

# pandasのバージョンを表示
pandas.__version__
'2.1.1'
Hide code cell source
# plotlyのうち、expressのみをインポート
import plotly.express

# 以下、散布図を描画するためのサンプルコード
# pandasでダミーのデータフレームを作成
df = pandas.DataFrame(
    {
        "X": [1, 2, 3, 4, 5, 6],
        "Y": [10, 15, 13, 17, 12, 18],
        "Category": ["A", "A", "B", "B", "A", "B"],
    }
)

# X軸としてX列、Y軸としてY列、Category列で色分けした散布図を作成
fig = plotly.express.scatter(
    df,
    x="X",
    y="Y",
    color="Category",
)

# 先に定義した関数で散布図を表示
show_fig(fig)

asで別名をつける場合#

この方法では、モジュールやパッケージを略称でインポートします。 Pythonでよく使われるライブラリには、コミュニティ内で広く受け入れられている慣習的な略称が存在します。 これらの略称を使うことで、コードの可読性を保ちつつ、効率的にコーディングすることができます。 以下に、いくつかの例を挙げます。

  • NumPy: 数値計算をサポートするライブラリ。import numpy as npと略称npを用いてインポートされることが多い

  • Pandas: データ分析をサポートするライブラリ。import pandas as pdと略称pdを用いてインポートされることが多い

  • Plotly: 本書で主に扱う可視化用のライブラリ。

    • express: 高レベルインターフェース。import plotly.express as pxと略称pxを用いてインポートされることが多い

    • graph_objects: 低レベルインターフェース。import plotly.graph_objects as goと略称goを用いてインポートされることが多い

本書でも、この慣習に従ってインポートします。

Hide code cell source
# pandasをpdという名前でインポート
import pandas as pd

# plotly.expressをpxという名前でインポート
import plotly.express as px

# 以下、散布図を描画するためのサンプルコード
# pandasでダミーのデータフレームを作成
df = pd.DataFrame(
    {
        "X": [1, 2, 3, 4, 5, 6],
        "Y": [10, 15, 13, 17, 12, 18],
        "Category": ["A", "A", "B", "B", "A", "B"],
    }
)

# X軸としてX列、Y軸としてY列、Category列で色分けした散布図を作成
fig = px.scatter(df, x="X", y="Y", color="Category")

# 先に定義した関数で散布図を表示
show_fig(fig)

fromを使う場合#

この方法では、モジュールやパッケージから特定の関数やクラスだけを選択してインポートします。 これを使うと、関数やクラスを{モジュール名}.{関数名}ではなく、直接{関数名}で呼び出すことができます。

本書では、例えばplotly.subplotsからmake_subplotsをインポートする際に用います。

Hide code cell source
# plotly.subplotsモジュールからmake_subplots関数をインポート
from plotly.subplots import make_subplots

# 以下、サブプロットを用いた散布図を描画するためのサンプルコード
# Category別に2列のサブプロットを持つ図を作成
fig = make_subplots(cols=2, subplot_titles=df["Category"].unique())

# df(DataFrame)を"Category"でグループ化し、各カテゴリごとに処理
for i, (cat, df_cat) in enumerate(df.groupby("Category")):
    # グループ化されたデータに対して散布図を作成
    subplot = px.scatter(df_cat, x="X", y="Y")
    # 散布図の各トレース(点の集まり)をfigに追加
    for trace in subplot.data:
        # add_traceメソッドを使用して、トレースを図に追加
        # i+1で列の位置を指定(列番号は1から始まるため)
        fig.add_trace(trace, col=i+1, row=1)

# 最終的に作成した図を表示
show_fig(fig)

標準ライブラリ#

Pythonの標準ライブラリには、様々な用途に応じた便利な機能が多数用意されています。 ここでは、本書でも活用するpathlibitertoolsについて解説します。

pathlib#

pathlibは、Python3.4から追加された、ファイルやディレクトリのパス操作を行うためのライブラリです。 かつてパスの操作に利用されてきたos.pathモジュールと比較し、pathlibはより直感的で使いやすいインターフェースを提供しています。

Hide code cell content
from pathlib import Path

# カレントディレクトリのパスを取得
dir_current = Path.cwd()
print(dir_current)  

# ファイルパスを作成
path_new = dir_current / "data" / "input.csv"
print(path_new) 
/home/jovyan/work/book/03
/home/jovyan/work/book/03/data/input.csv

Pathクラスを使うことで、パスの作成や結合が簡単に行えます。 また、Pathオブジェクトには、ファイルやディレクトリの存在確認、読み書きなどの操作も用意されています。

Pathクラスでは、/演算子を使ってパスを結合することができます。 この演算子は、Operating System(OS)に依存しないパス区切り文字を自動的に使用してくれるため、異なるOS間でもプログラムを移植しやすく[5]なります。

itertools#

itertoolsは、イテレータを効率的に操作するための関数を提供するライブラリです。 イテレータとは、要素を順次取り出すことができるオブジェクトのことで、Pythonではリストやタプルなどがイテレータに該当します。

Hide code cell content
import itertools

# リストの要素を組み合わせて全パターンを生成
colors = ["red", "blue", "green"]
sizes = ["small", "medium", "large"]

# 全ての組合せを順番に表示
for comb in itertools.product(colors, sizes):
    print(comb)
('red', 'small')
('red', 'medium')
('red', 'large')
('blue', 'small')
('blue', 'medium')
('blue', 'large')
('green', 'small')
('green', 'medium')
('green', 'large')

上記の例では、product関数を使って二つのリストの要素を組み合わせて全パターンを生成しています。 こういった処理は単純に見えますが、自分で実装しようと思うと大変なものです。 itertoolsは他にも様々な関数を提供してみますので、イテレータに関する処理を検討する場合は、一度確認してみることをおすすめします。